//! JSON Web Token generation and verification lightweight library, hash algorithm currently only supports hs256
//!
//! author: kiven lee
//! date: 2023-07-01

use std::error::Error as StdError;
use std::time::SystemTime;

use base64::{engine::general_purpose, Engine};
use hmac::{Hmac, Mac};
use memchr::{memchr_iter, memrchr};
#[cfg(feature = "rsa")]
use rsa::{
    pkcs1::{DecodeRsaPrivateKey, DecodeRsaPublicKey},
    pkcs1v15::{Signature, SigningKey, VerifyingKey},
    signature::SignatureEncoding,
    signature::{Signer, Verifier},
};
use serde::de::DeserializeOwned;
use serde_json::{Map, Value};
use sha2::Sha256;
use smallvec::SmallVec;
use thiserror::Error;

#[cfg(feature = "rsa")]
pub use rsa::{RsaPrivateKey, RsaPublicKey};

type Result<T> = std::result::Result<T, JwtError>;
type HmacSha256 = Hmac<Sha256>;
type StackVec = SmallVec<[u8; 512]>;

macro_rules! jwt_error {
    ($msg:expr) => {
        JwtError::new($msg, file!(), line!())
    };

    ($msg:expr, $err:ident) => {
        JwtError::with_source($msg, file!(), line!(), $err)
    };
}

#[derive(Error, Debug)]
#[error("{message} at {file}:{line}")]
pub struct JwtError {
    message: String,
    file: &'static str,
    line: u32,
    #[source]
    source: Option<Box<dyn StdError + Send + Sync + 'static>>,
}

impl JwtError {
    pub fn new(message: String, file: &'static str, line: u32) -> Self {
        JwtError {
            message,
            file,
            line,
            source: None,
        }
    }

    pub fn with_source<E>(message: String, file: &'static str, line: u32, source: E) -> Self
    where
        E: std::error::Error + Send + Sync + 'static,
    {
        JwtError {
            message,
            file,
            line,
            source: Some(Box::new(source)),
        }
    }
}

pub const AUTHORIZATION: &str = "Authorization";
pub const BEARER: &str = "Bearer ";
pub const WWW_AUTHENTICATE: &str = "WWW-Authenticate";
/// jwt body: 发布者的键名
pub const ISSUER_KEY: &str = "iss";
/// jwt body: 过期时间的键名
pub const EXP_KEY: &str = "exp";

/// jwt头部: json字符串的base64编码: {"alg":"HS256","typ":"JWT"}
const HEADER_B64: &str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";

#[cfg(feature = "rsa")]
/// {"alg":"RS256","typ":"JWT"}
const HEADER_RS256_B64: &str = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9";

/// Generate a jwt using the specified parameters, jwt type is HS256
///
/// * `claims`: jwt user custom data, it must be Value::Object or Value::Null
/// * `key`: jwt hmac encrypt key
/// * `issuer` jwt issuer value
/// * `exp`: jwt expire time value(Unit: second), 1 hour like 3600
///
/// Returns:
///
/// Ok(String): jwt string, Err(e): error
///
/// # Examples
///
/// ```rust
/// use jwt;
///
/// let s = jwt::encode(&serde_json::json!({
///     "userId": 1,
///     "username": "kiven",
/// }), "password", "my_app_name", 86400).unwrap();
/// ```
pub fn encode(claims: Value, key: &str, issuer: &str, ttl: u64) -> Result<String> {
    // 复制claims，并添加 issuer 和 exp 属性，形成最终的 claims 条目
    let claims_json = merge_claims(claims, issuer, ttl)?;
    encode_raw(&claims_json, key)
}

/// Generate a jwt using the specified parameters, jwt type is HS256
///
/// * `claims`: jwt user custom data as json string
/// * `key`: jwt hmac encrypt key
///
/// Returns:
///
/// Ok(String): jwt string, Err(e): error
///
/// ```
pub fn encode_raw(claims: &str, key: &str) -> Result<String> {
    let mut jwt_str = format!("{}.{}", HEADER_B64, claims);
    // 计算jwt的签名，即 header_base64 + "." + claims_base64 的签名，并转成base64编码
    let mut hs256 = HmacSha256::new_from_slice(key.as_bytes())
        .map_err(|e| jwt_error!("hmac sha256 error".to_string(), e))?;
    hs256.update(jwt_str.as_bytes());
    let sign_bs = hs256.finalize();
    let sign_b64 = general_purpose::URL_SAFE_NO_PAD.encode(sign_bs.into_bytes());

    // 生成最终的jwt字符串: header_base64 + "." + claims_base64 + "." + sign_base64
    jwt_str.push('.');
    jwt_str.push_str(&sign_b64);

    Ok(jwt_str)
}

/// Parsing and verifying jwt string, using jwt type HS256
///
/// * `jwt`: jwt string
/// * `key`: jwt hmac encrypt key
/// * `issuer` jwt issuer value
///
/// Returns:
///
/// Ok(String): jwt string, Err(e): error
///
/// # Examples
///
/// ```rust
/// let jwt_str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOi\
///     JhY2NpbmZvIiwidXNlciI6ImtpdmVuIiwiZXhwIjoxNTE2MjM5MDIyfQ.t\
///     dcZYbN7tavs9LdbfZT7R1SJeu75FVHvtljm8gjNGig";
///
/// let s = jwt::decode(&jwt_str, "password", "accinfo").unwrap();
///
/// assert_eq!("kiven", s["user"].as_str());
/// ```
pub fn decode(jwt: &str, key: &str, issuer: &str) -> Result<Value> {
    decode_custom(jwt, key, issuer, get_iss_exp_by_value)
}

/// Parsing and verifying jwt string, using jwt type HS256
///
/// * `jwt`: jwt string
/// * `key`: jwt hmac encrypt key
/// * `issuer` jwt issuer value
/// * `get_iss_exp` function for get iss and exp from result value
///
pub fn decode_custom<T: DeserializeOwned>(
    jwt: &str,
    key: &str,
    issuer: &str,
    get_iss_exp: fn(&T) -> (Option<&str>, Option<u64>),
) -> Result<T> {
    if key.is_empty() {
        return Err(jwt_error!("jwt key is empty".to_string()));
    }

    // 把jwt按‘.'分为3段
    let jwt = jwt.as_bytes();
    let sp_ret = jwt_split_slice(jwt).map_err(|e| jwt_error!("jwt_split error".to_string(), e))?;
    let (header_b64, claims_b64, sign_b64) = sp_ret;

    // 校验头部内容是否正确
    if HEADER_B64.as_bytes() != header_b64 {
        return Err(jwt_error!("token header error".to_string()));
    }

    // 反序列化claims
    let claims_str = decode_base64_slice(claims_b64)?;
    let claims: T = serde_json::from_slice(&claims_str)
        .map_err(|e| jwt_error!("deerialze json error".to_string(), e))?;

    let (iss, exp) = get_iss_exp(&claims);
    // 校验发行者和过期时间
    check_claims(issuer, &iss, &exp)?;

    let mut header_claims_b64 = StackVec::new();
    header_claims_b64.extend_from_slice(header_b64);
    header_claims_b64.push(b'.');
    header_claims_b64.extend_from_slice(claims_b64);

    // 校验签名
    let sign_bs = decode_base64_slice(sign_b64)?;
    let mut hs256 = HmacSha256::new_from_slice(key.as_bytes())
        .map_err(|e| jwt_error!("hmac sha256 error".to_string(), e))?;
    hs256.update(&header_claims_b64);

    if let Err(e) = hs256.verify_slice(&sign_bs) {
        return Err(jwt_error!("Signature verification failed".to_string(), e));
    }

    Ok(claims)
}

/// Generate a jwt using the specified parameters, jwt type is RS256
///
/// * `claims`: jwt user custom data, it must be Value::Object or Value::Null
/// * `issuer` jwt issuer value
/// * `exp`: jwt expire time value(Unit: second), 1 hour like 3600
///
/// Returns:
///
/// Ok(String): jwt string, Err(e): error
///
/// # Examples
///
/// ```rust
/// use jwt;
///
/// let s = jwt::encode_with_rsa(&serde_json::json!({
///     "userId": 1,
///     "username": "kiven",
/// }), "password", "my_app_name", 86400).unwrap();
/// ```
#[cfg(feature = "rsa")]
pub fn encode_with_rsa_default(claims: Value, issuer: &str, ttl: u64) -> Result<String> {
    let pri_key = RsaPrivateKey::from_pkcs1_pem(rsa_key_data::RSA_PRIVATE_KEY)
        .map_err(|e| jwt_error!("load private key from pkcs1 pem failed".to_string(), e))?;
    encode_with_rsa(claims, pri_key, issuer, ttl)
}

#[cfg(feature = "rsa")]
pub fn encode_with_rsa(claims: Value, pri_key: RsaPrivateKey, issuer: &str, ttl: u64) -> Result<String> {
    // 复制claims，并添加 issuer 和 exp 属性，形成最终的 claims 条目
    let claims_json = merge_claims(claims, issuer, ttl)?;
    encode_with_rsa_raw(&claims_json, pri_key)
}

#[cfg(feature = "rsa")]
pub fn encode_with_rsa_raw(claims: &str, pri_key: RsaPrivateKey) -> Result<String> {
    // 复制claims，并添加 issuer 和 exp 属性，形成最终的 claims 条目
    let mut jwt_str = format!("{}.{}", HEADER_RS256_B64, claims);

    // 计算jwt的签名，即 header_base64 + "." + claims_base64 的签名，并转成base64编码
    // 签名算法：
    //    1. 先用 sha256 计算 header_base64 + "." + claims_base64 的结果
    //    2. 再用 rsa sign 计算上一个步骤的结果
    //    3. 把rsa计算结果用base64编码，就是最终的签名
    // let pri_key = RsaPrivateKey::from_pkcs1_pem(pri_key)
    //     .map_err(|e| jwt_error!("load private key from pkcs1 pem failed".to_string(), e))?;
    let rsa_sign = SigningKey::<Sha256>::new(pri_key);
    let sign_bs: Signature = rsa_sign.sign(jwt_str.as_bytes());
    let sign_b64 = general_purpose::URL_SAFE_NO_PAD.encode(sign_bs.to_bytes());

    // 生成最终的jwt字符串: header_base64 + "." + claims_base64 + "." + sign_base64
    jwt_str.push('.');
    jwt_str.push_str(&sign_b64);

    Ok(jwt_str)
}

/// Parsing and verifying jwt string, using jwt type RS256
///
/// * `jwt`: jwt string
/// * `issuer` jwt issuer value
///
/// Returns:
///
/// Ok(String): jwt string, Err(e): error
///
/// # Examples
///
/// ```rust
/// use jwt;
///
/// let jwt_str = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhY2NpbmZv\
///     IiwidXNlciI6ImtpdmVuIiwiZXhwIjoxNTE2MjM5MDIyfQ.pbpeMJL4ZPZ-vmf3jgtWKT\
///     gvg7erJrjsFhKTd92NvIqe6nO1SESf92YZLF6G-Dj0k8wfitEvuEQH190xSdGuHMMM-QH\
///     _wWIGJCSG6G6QFjB3c5ZxePEqdY118LNp0AhA9odJiy9cZ6tFDJiyWX1aq9rBnYFnEErc\
///     w_m\ZyLC-PC7k9g6IgT7BaTe7_FOkI8y74RA8SMmFELAfwULB1bZaDZ0SLfMzvv8lwAY6\
///     UiPvQso-eGVTPnc1YuIO154Fg9Se2eh_hU6Ktwwl6VBWpikE-TxExfUsD8dmNtL5b3QNe\
///     1Hf17UfeYG5PmNbQsg1ybDlLqyP68Q1Vvfr_54Bu8szfw";
///
/// let s = jwt::decode(&jwt_str, "accinfo").unwrap();
///
/// assert_eq!("kiven", s["user"].as_str());
/// ```
#[cfg(feature = "rsa")]
pub fn decode_with_rsa_default(jwt: &str, issuer: &str) -> Result<Value> {
    let pub_key = RsaPublicKey::from_pkcs1_pem(rsa_key_data::RSA_PUBLIC_KEY)
        .map_err(|e| jwt_error!("load public key from pkcs1 pem failed".to_string(), e))?;
    decode_with_rsa(jwt, pub_key, issuer)
}

/// Parsing and verifying jwt string, using jwt type RS256
///
/// * `jwt`: jwt string
/// * `public_key`: jwt hmac encrypt key
/// * `issuer` jwt issuer value
/// * `get_iss_exp` function for get iss and exp from result value
///
#[cfg(feature = "rsa")]
pub fn decode_with_rsa(jwt: &str, pub_key: RsaPublicKey, issuer: &str) -> Result<Value> {
    decode_custom_with_rsa(jwt, pub_key, issuer, get_iss_exp_by_value)
}

/// Parsing and verifying jwt string, using jwt type RS256
///
/// * `jwt`: jwt string
/// * `public_key`: jwt hmac encrypt key
/// * `issuer` jwt issuer value
/// * `get_iss_exp` function for get iss and exp from result value
///
#[cfg(feature = "rsa")]
pub fn decode_custom_with_rsa<T: DeserializeOwned>(
    jwt: &str,
    pub_key: RsaPublicKey,
    issuer: &str,
    get_iss_exp: fn(&T) -> (Option<&str>, Option<u64>),
) -> Result<T> {
    // 把jwt按‘.'分为3段
    let jwt = jwt.as_bytes();
    let sp_ret = jwt_split_slice(jwt).map_err(|e| jwt_error!("jwt_split error".to_string(), e))?;
    let (header_b64, claims_b64, sign_b64) = sp_ret;

    // 校验头部内容是否正确
    if HEADER_RS256_B64.as_bytes() != header_b64 {
        return Err(jwt_error!("token header error".to_string()));
    }

    // 反序列化claims
    let claims_str = decode_base64_slice(claims_b64)?;
    let claims: T = serde_json::from_slice(&claims_str)
        .map_err(|e| jwt_error!("deerialze json error".to_string(), e))?;

    let (iss, exp) = get_iss_exp(&claims);
    // 校验发行者和过期时间
    check_claims(issuer, &iss, &exp)?;

    let mut header_claims_b64 = StackVec::new();
    header_claims_b64.extend_from_slice(header_b64);
    header_claims_b64.push(b'.');
    header_claims_b64.extend_from_slice(claims_b64);

    // 校验签名
    let sign_bs = decode_base64_slice(sign_b64)?;
    // let pub_key = RsaPublicKey::from_pkcs1_pem(public_key)
    //     .map_err(|e| jwt_error!("load public key from pkcs1 pem failed".to_string(), e))?;
    let rsa_verify = VerifyingKey::<Sha256>::new(pub_key);
    let sign = Signature::try_from(sign_bs.as_ref())
        .map_err(|e| jwt_error!("rsa sign data error".to_string(), e))?;
    if let Err(e) = rsa_verify.verify(&header_claims_b64, &sign) {
        return Err(jwt_error!("Signature verification failed".to_string(), e));
    }

    Ok(claims)
}

// split the jwt into three str, it's header, claims, sign
pub fn jwt_split(jwt: &str) -> Result<(&str, &str, &str)> {
    use std::str::from_utf8;
    const UTF8_ERR: &str = "slice is not utf8";

    let (s1, s2, s3) = jwt_split_slice(jwt.as_bytes())
        .map_err(|e| jwt_error!("jwt_split error".to_string(), e))?;

    let s1 = from_utf8(s1).map_err(|_| jwt_error!(UTF8_ERR.to_string()))?;
    let s2 = from_utf8(s2).map_err(|_| jwt_error!(UTF8_ERR.to_string()))?;
    let s3 = from_utf8(s3).map_err(|_| jwt_error!(UTF8_ERR.to_string()))?;

    Ok((s1, s2, s3))
}

// split the jwt slice<u8> into three slice<u8>, it's header, claims, sign
pub fn jwt_split_slice(jwt: &[u8]) -> Result<(&[u8], &[u8], &[u8])> {
    let mut find_iter = memchr_iter(b'.', jwt);
    let first_pos = match find_iter.next() {
        Some(n) => n,
        None => return Err(jwt_error!("token find '.' not found".to_string())),
    };
    let second_pos = match find_iter.next() {
        Some(n) => n,
        None => return Err(jwt_error!("token find '.' not found".to_string())),
    };

    let header_b64 = &jwt[..first_pos];
    let claims_b64 = &jwt[first_pos + 1..second_pos];
    let sign_b64 = &jwt[second_pos + 1..];

    Ok((header_b64, claims_b64, sign_b64))
}

/// Parsing jwt string and return sign base64 string
pub fn get_sign(jwt: &str) -> Option<&str> {
    if let Some(pos) = memrchr(b'.', jwt.as_bytes()) {
        if pos < jwt.len() - 1 {
            return Some(&jwt[pos + 1..]);
        }
    }
    None
}

/// get the issuer from jwt token
///
/// * `claims`: jwt user custom data, it must be Value::Object or Value::Null
///
/// Returns:
///
/// Ok(()): verfied success, Err(e): error
///
pub fn get_issuer(claims: &Value) -> Result<&str> {
    match claims.get(ISSUER_KEY) {
        Some(Value::String(iss)) => Ok(iss),
        _ => Err(jwt_error!("issuer not found".to_string())),
    }
}

/// get expire from jwt token
///
/// ### Arguments
///
/// * `claims`: jwt user custom data, it must be Value::Object or Value::Null
///
/// ### Returns
///
/// Ok(String): verfied success, Err(e): error
///
pub fn get_exp(claims: &Value) -> Result<u64> {
    match claims.get(EXP_KEY) {
        Some(exp) => match exp.as_u64() {
            Some(exp) => Ok(exp),
            None => Err(jwt_error!(format!("exp format error: {}", exp))),
        },
        None => Err(jwt_error!("not found exp".to_string())),
    }
}

/// Verify whether the issuer is correct from jwt token
///
/// ### Arguments
///
/// * `claims`: jwt user custom data, it must be Value::Object or Value::Null
/// * `issuer` jwt issuer value
///
/// ### Returns
///
/// Ok(()): verfied success, Err(e): error
///
pub fn check_issuer<'a>(claims: &'a Value, issuer: &str) -> Result<&'a str> {
    if let Some(Value::String(iss)) = claims.get(ISSUER_KEY) {
        // 校验成功，直接返回
        if issuer.is_empty() || issuer == iss {
            Ok(iss)
        } else {
            let msg = format!("incorrect issuer, expected: [{}], actual: [{}]", issuer, iss);
            Err(jwt_error!(msg))
        }
    } else if issuer.is_empty() {
        Ok("")
    } else {
        Err(jwt_error!("not found issuer".to_string()))
    }
}

/// Verify whether expire from jwt token
///
/// * `claims`: jwt user custom data, it must be Value::Object or Value::Null
///
/// Returns:
///
/// Ok(String): verfied success, Err(e): error
///
pub fn check_exp(claims: &Value) -> Result<u64> {
    match claims.get(EXP_KEY) {
        Some(exp) => {
            match exp.as_u64() {
                Some(exp) => {
                    let now = unix_timestamp_after(0)?;
                    if exp >= now {
                        Ok(exp)
                    } else {
                        Err(jwt_error!("incorrect exp".to_string()))
                    }
                }
                None => Err(jwt_error!("exp format error".to_string()))
            }
        }
        None => Err(jwt_error!("exp not found".to_string()))
    }
}

/// verify claims iss and exp, use issuer
pub fn check_claims(issuer: &str, iss: &Option<&str>, exp: &Option<u64>) -> Result<()> {
    // 校验发行者（如果需要的话）
    if !issuer.is_empty() {
        match iss {
            Some(iss) => if *iss != issuer {
                let msg = format!("incorrect issuer, expected: [{issuer}], actual: [{iss}]");
                return Err(jwt_error!(msg));
            }
            None => {
                return Err(jwt_error!("jwt token iss not found".to_string()));
            }
        }
    }
    // 校验过期时间
    match exp {
        Some(exp) => if *exp < unix_timestamp_after(0)? {
            return Err(jwt_error!("Incorrect exp".to_string()));
        },
        None => return Err(jwt_error!("jwt token exp not found".to_string())),
    }

    Ok(())
}

pub fn decode_base64(base64: &str) -> Result<Vec<u8>> {
    decode_base64_slice(base64.as_bytes())
}

pub fn decode_base64_slice(base64: &[u8]) -> Result<Vec<u8>> {
    general_purpose::URL_SAFE_NO_PAD
        .decode(base64)
        .map_err(|e| jwt_error!("base64 decode error".to_string(), e))
}

/// get unix timestamp base now + after_now
pub fn unix_timestamp_after(after_now: u64) -> Result<u64> {
    SystemTime::now()
        .duration_since(SystemTime::UNIX_EPOCH)
        .map(|v| v.as_secs() + after_now)
        .map_err(|_| jwt_error!("unix_timestamp error".to_string()))
}

fn merge_claims(claims: Value, issuer: &str, ttl: u64) -> Result<String> {
    debug_assert!(claims.is_null() || claims.is_object());

    let exp = unix_timestamp_after(ttl)?;
    let mut claims = value_to_map(claims)?;

    if !issuer.is_empty() {
        claims.insert(ISSUER_KEY.to_string(), issuer.into());
    }
    claims.insert(EXP_KEY.to_owned(), Value::Number(exp.into()));

    // 将claims转成json后用base64编码
    let claims_bs = claims_to_json(&claims)?;
    let claims_b64 = general_purpose::URL_SAFE_NO_PAD.encode(claims_bs);

    Ok(claims_b64)
}

fn value_to_map(val: Value) -> Result<Map<String, Value>> {
    match val {
        Value::Null => Ok(Map::new()),
        Value::Object(v) => Ok(v),
        _ => Err(jwt_error!("token claims format error".to_string())),
    }
}

fn claims_to_json(claims: &Map<String, Value>) -> Result<Vec<u8>> {
    serde_json::to_vec(&claims)
        .map_err(|e| jwt_error!("serialze claims to json failed".to_string(), e))
}

fn get_iss_exp_by_value(claims: &Value) -> (Option<&str>, Option<u64>) {
    let iss = match claims.get(ISSUER_KEY) {
        Some(Value::String(iss)) => Some(iss.as_str()),
        _ => None,
    };
    let exp = claims.get(EXP_KEY).and_then(|v| v.as_u64());

    (iss, exp)
}


#[cfg(feature = "rsa")]
mod rsa_key_data {
    pub const RSA_PRIVATE_KEY: &str = r#"
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEA1h9pD+0KYp5Bpda/OTWFVxaXKPO4+36LzNWk53PG4LOrrg2o
rJzdbhFwoqB20ceFQOZm9dK8udY3LFn4Pv1M01pkPRV39+URLds3W+CujnTQiCJ/
vBeWrIf7HYq6TM0oQcmzESvRsVf37xZpvlK21pxgsxg2pyYoqZrx24ttxm81ZtJj
v76QmzbaU3Lz+rYOfwxzeQelXZ0KDxWrptm+FlIspUzSGYxV6RmV66svaxzNi8mi
bQuIx8BWbVHGWU45cISC4+oSnqUirB8i/URgZhwHyYfFO/Tmmf/+NROjXAqbH7lr
bJBtAP+OXYuKfkBNLUqRmXax2uttbaefF0sxQQIDAQABAoIBADEKbKOrJK/Fkz+K
Wa2epnV1xRUqDPn818QIQoaIK8qXHAD3O+Sc4NIuyF9W5R/S1KAypO40X+koOOa9
jG/Qz+GwWDjtS9bI7hBUnu86HICgHIqxbBQGSwok8synU1f3vPqkWZDbOmGlxjFK
LtnaU+n/Ut5x80KBKNr/k9k2q+PAdWrna/LfcjXtVqkZVUl/3xD+Dp7IIM7nQQuX
Lnfvyk86gP+KKUPs23cnPFwqpWibJO7swJ9tlHQXOefVurvCJFzFhjyG8CkpMcn9
R1B9uUZGGTOYrD797vJ3Ll5JM9Wg+9cy2C8ovOGvEY0L6Y9UhGUqESVjYnXM9/bp
tixAG2ECgYEA8elVewkf29b0/5bHNiGimS/6Qo3/Hz4Ijb5xr9ye7LrbjaQbeDAW
4RwnDvz11zJAR9wRKtZApCGH5NEexZykdxjmxQGOGTz/PUpsRhXFpDW+u4BrwHBZ
3l4j/xoOxQbxIvE3hSZLg22FZVhwfWmBOM4P8L9V34IX8awqyYCBx7UCgYEA4pfF
22e7gtbjqkIlTh8ekZJi3obWdOFCR6r+4ySeUG+b3bL91Js0/2RIu5wB0+t9OZ5d
F7N54e0S2p5au2YcD8q1DmJj8b0tgOffHcCgGWnIhDtu6QA8NKxR3lWYHzJTq9Ju
ZhY5yqopXYOhifBQoliAWFecrgh4pqKFTzGk4t0CgYB6t+GzPpe40D0NA5IfdcSk
bWBJLvuC/9cbAMdvbT353XjPS7bbq5mPrNZrlguolUdirNLQpku4d4IWo7c2jBYq
jKlUu0s4pmbc0spGa3kNqm4NdEI1J0mPsrYUDUX80V62WSPPGfQowgBvvwOhu0ng
ZThU6ttHPRmkcbBq9BPiGQKBgGvZY1IHsIcY8qmB7DGfvDP7YdWahg6BfMORztmb
/0I3rQ87d3cvHG2GdNve6DvOtP6sspBqW1O+PCAUCQlzE14s1DpxeDKCIVtegaKu
oUUXRVoy05pRA1bqwdi6ErqegJaihOtQHteoYCHjWgrGeAqdZxElOizXWV2usxa7
gUh9AoGAZiIlLmGG0dDBvWCWjs0oiPCSIpeINtbNEUIZ5CtI9pUAdR2kMOUIRbta
VlGTzzTlP/uGZlLqRhe6QnLii0MeR7B6suM9JHg0bcLN0diwYpiit73+el1KJYwg
1aM3mKtopVX0gWXIAYbgPDGfxCR/0SbK1XVbBEHthO9Ns563CAU=
-----END RSA PRIVATE KEY-----
"#;

    pub const RSA_PUBLIC_KEY: &str = r#"
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEA1h9pD+0KYp5Bpda/OTWFVxaXKPO4+36LzNWk53PG4LOrrg2orJzd
bhFwoqB20ceFQOZm9dK8udY3LFn4Pv1M01pkPRV39+URLds3W+CujnTQiCJ/vBeW
rIf7HYq6TM0oQcmzESvRsVf37xZpvlK21pxgsxg2pyYoqZrx24ttxm81ZtJjv76Q
mzbaU3Lz+rYOfwxzeQelXZ0KDxWrptm+FlIspUzSGYxV6RmV66svaxzNi8mibQuI
x8BWbVHGWU45cISC4+oSnqUirB8i/URgZhwHyYfFO/Tmmf/+NROjXAqbH7lrbJBt
AP+OXYuKfkBNLUqRmXax2uttbaefF0sxQQIDAQAB
-----END RSA PUBLIC KEY-----
"#;

    #[allow(dead_code)]
    pub const PUBLIC_KEY: &str = r#"
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1h9pD+0KYp5Bpda/OTWF
VxaXKPO4+36LzNWk53PG4LOrrg2orJzdbhFwoqB20ceFQOZm9dK8udY3LFn4Pv1M
01pkPRV39+URLds3W+CujnTQiCJ/vBeWrIf7HYq6TM0oQcmzESvRsVf37xZpvlK2
1pxgsxg2pyYoqZrx24ttxm81ZtJjv76QmzbaU3Lz+rYOfwxzeQelXZ0KDxWrptm+
FlIspUzSGYxV6RmV66svaxzNi8mibQuIx8BWbVHGWU45cISC4+oSnqUirB8i/URg
ZhwHyYfFO/Tmmf/+NROjXAqbH7lrbJBtAP+OXYuKfkBNLUqRmXax2uttbaefF0sx
QQIDAQAB
-----END PUBLIC KEY-----
"#;
}
